/*
 * Copyright (C) 2011-2021 ShenZhen iBOXCHAIN Information Technology Co.,Ltd.
 *
 * All right reserved.
 *
 * This software is the confidential and proprietary
 * information of iBOXCHAIN Company of China.
 * ("Confidential Information"). You shall not disclose
 * such Confidential Information and shall use it only
 * in accordance with the terms of the contract agreement
 * you entered into with iBOXCHAIN inc.
 *
 */
package cn.anoxia.springframework.beans.support;

import cn.anoxia.springframework.beans.BeanFactory;
import cn.anoxia.springframework.beans.BeansException;
import cn.anoxia.springframework.beans.factory.BeanDefinition;
import cn.anoxia.springframework.beans.factory.FactoryBean;
import cn.anoxia.springframework.beans.factory.config.BeanPostProcessor;
import cn.anoxia.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;

import java.util.ArrayList;
import java.util.List;

/**
 * @author huangle
 */
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {

  private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

  @Override
  public Object getBean(String name) throws BeansException {
    return doGetBean(name,null);
  }

  @Override
  public Object getBean(String name, Object... args) throws BeansException {
    return doGetBean(name,args);
  }

//  public Object doGetBean(String name, Object... args) throws BeansException {
//    Object singleton = getSingleton(name);
//    if (singleton != null){
//      return singleton;
//    }
//    BeanDefinition definition = getBeanDefinition(name);
//    return createBean(name,definition,args);
//  }

  protected <T> T doGetBean(final String name, final Object[] args) throws BeansException {
    Object singleton = getSingleton(name);
    // 获取单例bean
    if (singleton != null){
      // 直接去缓存中获取
      return (T) getObjectForBeanInstance(singleton,name);
    }
    // 先创建出来在去获取
    BeanDefinition beanDefinition = getBeanDefinition(name);
    Object bean = createBean(name, beanDefinition, args);
    return (T) getObjectForBeanInstance(bean,name);
  }

  private Object getObjectForBeanInstance(Object beanInstance,String beanName) throws BeansException {
    // 如果这个bean 不是 FactoryBean ，直接返回
    if (!(beanInstance instanceof FactoryBean)){
      return beanInstance;
    }
    Object object = getCachedObjectForFactoryBean(beanName);
    if (object == null){
      FactoryBean<?> factoryBean = (FactoryBean<?>) beanInstance;
      object = getObjectFromFactoryBean(factoryBean,beanName);
    }
    return object;
  }

  @Override
  public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
    return (T) getBean(name);
  }

  /**
   * 获取beanDefinition
   * @param name
   * @return {@link BeanDefinition}
   * @throws
   * @author huangle
   * @date 2021/8/23
   */
  protected abstract BeanDefinition getBeanDefinition(String name) throws BeansException;

  protected abstract Object createBean(String name, BeanDefinition beanDefinition,Object[] args) throws BeansException;


  public List<BeanPostProcessor> getBeanPostProcessors() {
    return this.beanPostProcessors;
  }

  @Override
  public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor){
    this.beanPostProcessors.remove(beanPostProcessor);
    this.beanPostProcessors.add(beanPostProcessor);
  }

}
